Skip to content

Feature/offlevel#710

Open
eduardosmaniotto wants to merge 23 commits intoMUnique:masterfrom
eduardosmaniotto:feature/offlevel
Open

Feature/offlevel#710
eduardosmaniotto wants to merge 23 commits intoMUnique:masterfrom
eduardosmaniotto:feature/offlevel

Conversation

@eduardosmaniotto
Copy link
Contributor

@eduardosmaniotto eduardosmaniotto commented Mar 13, 2026

Feature: Offline Leveling (/offlevel command)
Allows players to continue grinding after disconnecting. When a player types /offlevel, their character stays in the world as a ghost and automatically fights monsters using the existing MU Helper configuration.
How it works:

  1. Player types /offlevel chat command
  2. The real client disconnects (player can reconnect anytime)
  3. A ghost player spawns at the same location and continues fighting using MU Helper
  4. When the player logs back in, the ghost automatically stops
    Technical details:
  • No network traffic to offline player: View plugin calls meant FOR the offline player are suppressed via NullViewProxy
  • Visible to other players: Attack animations are intentionally broadcast so other players can see the ghost fighting
  • Persists XP: Uses the same persistence context for the ghost, ensuring XP is saved

Feature: Add a "Refresh" button to admin interface to reload updated entities from the database.

Feature: Add a "Duplicate" button to inventory in admin interface.

PS. This also contains fixes from previous PR #706
PS2. This PR will not include party support, otherwise it would be too much to review.

@gemini-code-assist
Copy link

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces a significant new feature: offline leveling. Players can now use a chat command to leave their character actively grinding in the game world even after disconnecting, leveraging their MU Helper settings. This is achieved through a 'ghost' player system that handles combat, item pickup, and movement, while minimizing network overhead. The feature also includes automatic session termination upon player re-login and refactors MU Helper configurations for clarity. Additionally, a quality-of-life improvement for administrators has been added, allowing for easy duplication of items within the admin panel.

Highlights

  • Offline Leveling Feature: Introduced a new /offlevel command that allows players to disconnect their client while their character remains in the game world as a 'ghost' player, automatically fighting monsters using their existing MU Helper configuration. This ghost player is visible to others and persists XP gains.
  • Network Optimization for Ghost Players: Implemented a NullViewProxy mechanism to suppress all network traffic for offline leveling ghost players, ensuring efficient server resource usage while maintaining their presence in the game world.
  • Automated AI for Offline Leveling: Developed an OfflineLevelingIntelligence component that mimics the client-side MU Helper logic, enabling ghost players to perform attacks, use skills, manage health, pick up items, and regroup to their original position.
  • Seamless Re-login Integration: Added a plugin (OfflineLevelingStopOnLoginPlugIn) that automatically stops an active offline leveling session when the real player logs back into their account, allowing them to resume control of their character.
  • MU Helper Configuration Refactoring: Refactored MU Helper configuration classes, renaming MuHelperConfiguration to MuHelperServerConfiguration and introducing MuHelperPlayerConfiguration to better distinguish between server-side and player-specific settings.
  • Admin Panel Item Duplication: Enhanced the admin panel's item storage field with a new 'Duplicate' button, allowing administrators to easily create copies of existing items.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Changelog
  • src/AttributeSystem/StatAttribute.cs
    • Updated null-conditional operator for Definition.MaximumValue.HasValue check.
  • src/GameLogic/DroppedItem.cs
    • Changed item pickup log level from Information to Debug.
  • src/GameLogic/DroppedMoney.cs
    • Changed money pickup log level from Information to Debug.
  • src/GameLogic/GameContext.cs
    • Added OfflineLevelingManager property to manage offline leveling sessions.
  • src/GameLogic/GameMap.cs
    • Added GetDropsInRange method to retrieve dropped items and money within a specified range.
  • src/GameLogic/IGameContext.cs
    • Added OfflineLevelingManager property to the game context interface.
  • src/GameLogic/MuHelper/MuHelper.cs
    • Updated _configuration type from MuHelperConfiguration to MuHelperServerConfiguration.
  • src/GameLogic/MuHelper/MuHelperConfiguration.cs
    • Renamed MuHelperConfiguration.cs to MuHelperServerConfiguration.cs.
  • src/GameLogic/MuHelper/MuHelperFeaturePlugIn.cs
    • Updated configuration type from MuHelperConfiguration to MuHelperServerConfiguration.
  • src/GameLogic/MuHelper/MuHelperPlayerConfiguration.cs
    • Added new file defining the deserialized structure for player-specific MU Helper configurations.
  • src/GameLogic/OfflineLeveling/NullViewPlugInContainer.cs
    • Added new file for a view plugin container that creates NullViewProxy instances to suppress network traffic.
  • src/GameLogic/OfflineLeveling/NullViewProxy.cs
    • Added new file for a dynamic proxy that returns default values for method calls, effectively nullifying view plugin operations.
  • src/GameLogic/OfflineLeveling/OfflineLevelingIntelligence.cs
    • Added new file implementing the AI logic for offline leveling ghost players, including combat, healing, item pickup, and movement.
  • src/GameLogic/OfflineLeveling/OfflineLevelingManager.cs
    • Added new file to track and manage active offline leveling player instances.
  • src/GameLogic/OfflineLeveling/OfflineLevelingPlayer.cs
    • Added new file defining the OfflineLevelingPlayer class, representing a ghost player for offline leveling.
  • src/GameLogic/OfflineLeveling/OfflineLevelingStopOnLoginPlugIn.cs
    • Added new file for a plugin that stops an active offline leveling session when the associated account logs in.
  • src/GameLogic/Player.cs
    • Added SuppressDisconnectedEvent method to prevent event firing during offline leveling setup.
    • Removed trailing newline at the end of the file.
  • src/GameLogic/PlugIns/ChatCommands/Arguments/OfflineLevelingChatCommandPlugIn.cs
    • Added new file for the /offlevel chat command, enabling players to start offline leveling.
  • src/GameLogic/Properties/PlayerMessage.Designer.cs
    • Added new localized strings for offline leveling messages.
  • src/GameLogic/Properties/PlayerMessage.resx
    • Added new resource entries for offline leveling messages.
  • src/GameLogic/Properties/PlugInResources.Designer.cs
    • Added new localized strings for offline leveling plugin names and descriptions.
  • src/GameLogic/Properties/PlugInResources.resx
    • Added new resource entries for offline leveling plugin names and descriptions.
  • src/Persistence/Initialization/Updates/AddOfflineLevelingPlugIn.cs
    • Added new file to register the offline leveling chat command and stop-on-login plugins during database initialization.
  • src/Persistence/Initialization/Updates/UpdateVersion.cs
    • Added AddOfflineLeveling entry to the UpdateVersion enum.
  • src/Startup/Dockerfile
    • Added icu-libs installation and DOTNET_SYSTEM_GLOBALIZATION_INVARIANT environment variable for globalization support.
    • Refined Dockerfile build and publish steps, including better handling of project files and log directory creation.
  • src/Web/Shared/Components/Form/ItemStorageField.razor
    • Added a 'Duplicate' button to the item storage field in the admin panel.
  • src/Web/Shared/Components/Form/ValueListWrapper.cs
    • Modified Remove and RemoveAt methods to correctly handle item removal and index updates within the wrapper.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a significant new feature: offline leveling. The implementation is comprehensive, covering the AI for the offline character, state management, and integration with existing systems. The use of DispatchProxy to suppress network views is a clever approach. Additionally, the PR includes some refactoring and a new item duplication feature in the admin UI. My review identified a few areas for improvement: a minor file header inconsistency, a maintainability issue with a hardcoded string for skill identification, and a potential bug in the new item duplication logic that could lead to data issues. I've provided suggestions for each of these points.

@eduardosmaniotto eduardosmaniotto marked this pull request as draft March 13, 2026 15:24
@eduardosmaniotto eduardosmaniotto marked this pull request as ready for review March 14, 2026 00:40
@eduardosmaniotto eduardosmaniotto marked this pull request as draft March 14, 2026 00:40
Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a significant new feature: offline leveling. The implementation is comprehensive, including a ghost player entity, a server-side AI to control it, and plugins to manage its lifecycle. The approach to suppress network traffic for the ghost player using a null-view proxy is clever. Additionally, the PR includes UI enhancements like a 'Duplicate' button for items in the admin interface. While the core feature implementation is solid, I've identified a critical bug in the new 'Duplicate' item functionality that could lead to data corruption, along with a couple of medium-severity maintainability concerns in the new code that would be beneficial to address.

@eduardosmaniotto eduardosmaniotto marked this pull request as ready for review March 14, 2026 03:05
Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces the significant 'Offline Leveling' feature, allowing players to continue grinding after disconnecting. The implementation is comprehensive, including the AI for the offline player, management of sessions, and seamless integration with the login process. The use of a DispatchProxy to suppress network traffic for offline players is a clever approach. Additionally, the PR adds a 'Duplicate' item feature in the admin panel's inventory editor and a 'Refresh' button on edit pages. Several smaller refactorings improve code style and robustness. My review includes a couple of suggestions to enhance maintainability by refactoring large classes and better separating business logic from UI components. Overall, this is a solid contribution with well-executed features.

@eduardosmaniotto
Copy link
Contributor Author

/gemini review

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a significant new feature: offline leveling. The implementation is well-structured, creating a 'ghost' player driven by a server-side AI that mimics the MU Helper functionality. The use of a null-view proxy to suppress network traffic is a clever approach. The supporting changes, such as the admin panel refresh and duplicate item buttons, are also well-implemented. I've found one potential issue regarding the game's economy that should be considered.

@eduardosmaniotto eduardosmaniotto marked this pull request as draft March 14, 2026 04:32
@eduardosmaniotto eduardosmaniotto marked this pull request as ready for review March 14, 2026 20:23
Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a significant and well-implemented offline leveling feature, allowing players to continue progressing while disconnected. The implementation is robust, using a 'ghost' player with suppressed network views and a modular AI system for combat, buffing, and other actions. The addition of 'Refresh' and 'Duplicate' buttons to the admin panel are also valuable usability improvements. The code is generally of high quality with good structure and modern C# practices. I have one minor suggestion regarding a hardcoded future date in an update plugin.

@eduardosmaniotto eduardosmaniotto marked this pull request as draft March 14, 2026 22:41
@eduardosmaniotto eduardosmaniotto marked this pull request as ready for review March 15, 2026 17:26
Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces the significant 'Offline Leveling' feature, allowing players to continue progressing while disconnected. The implementation is well-structured, separating concerns into various handlers for combat, buffs, movement, and item management. The use of a 'ghost' player with a NullViewProxy to suppress network traffic is a clever and effective approach.

In addition to the main feature, the PR includes several valuable improvements:

  • A 'Refresh' button in the admin panel to reload data.
  • A 'Duplicate' button in the inventory editor for easier item management.
  • Refinements to the Dockerfile for better layer caching and configuration.
  • A bug fix in ValueListWrapper for safer item removal.

I have one suggestion for improving code clarity and safety in StatAttribute.cs by using a more modern C# pattern. Overall, this is a high-quality contribution with substantial new functionality and thoughtful improvements.

@eduardosmaniotto eduardosmaniotto marked this pull request as draft March 16, 2026 17:59
@eduardosmaniotto eduardosmaniotto marked this pull request as ready for review March 16, 2026 18:37
@eduardosmaniotto eduardosmaniotto requested a review from sven-n March 16, 2026 18:37
Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces the impressive offline leveling feature, allowing players to continue grinding after they disconnect. The implementation is comprehensive, creating a 'ghost' player that mimics the MU Helper's behavior through a well-structured set of new classes. The use of a NullViewPlugInContainer to suppress network traffic is a clever approach. The login logic has been thoughtfully refactored to handle session handovers from offline players. Additionally, the new 'Refresh' and 'Duplicate' buttons in the admin panel are welcome usability improvements. The overall code quality is high, with good attention to details like concurrency and resource management. I have one minor suggestion to enhance robustness.

}

var elapsed = DateTime.UtcNow - startTimestamp;
var currentStage = (int)(elapsed / configuration.StageInterval);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The division by configuration.StageInterval could lead to a DivideByZeroException if StageInterval is configured as TimeSpan.Zero. It would be safer to handle this case explicitly to prevent a potential crash.

        var currentStage = configuration.StageInterval > TimeSpan.Zero ? (int)(elapsed / configuration.StageInterval) : 0;

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please address this issue

Copy link
Member

@sven-n sven-n left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks almost perfect now!
I didn't review the new LoginAction yet, I need a clear mind for that, hopefully tomorrow 😅

}

var elapsed = DateTime.UtcNow - startTimestamp;
var currentStage = (int)(elapsed / configuration.StageInterval);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please address this issue

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we use the MUnique.OpenMU.GameLogic.PlayerActions.Items.ItemRepairAction here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes I've updated it, thanks for the suggestions.

@eduardosmaniotto eduardosmaniotto requested a review from sven-n March 18, 2026 00:26
@eduardosmaniotto
Copy link
Contributor Author

Looks almost perfect now! I didn't review the new LoginAction yet, I need a clear mind for that, hopefully tomorrow 😅

Thanks. It was basically a code extraction with the addition of this lines:

            var finalAccount = account;
            if (player.GameContext.OfflineLevelingManager.IsActive(username))
            {
                finalAccount = await this.HandleOfflineSessionHandoverAsync(player, username, password, account).ConfigureAwait(false);
                if (finalAccount is null)
                {
                    context.Allowed = false;
                    return (false, null);
                }
            }
    private async ValueTask<Account?> HandleOfflineSessionHandoverAsync(Player player, string username, string password, Account account)
    {
        player.Logger.LogInformation("Account {username} has an active offline session. Stopping it and reloading account data.", username);
        await player.GameContext.OfflineLevelingManager.StopAsync(username).ConfigureAwait(false);
        player.PersistenceContext.Detach(account);
        var reloadedAccount = await player.PersistenceContext.GetAccountByLoginNameAsync(username, password).ConfigureAwait(false);
        if (reloadedAccount is null)
        {
            player.Logger.LogError("Failed to reload account {username} after stopping offline session.", username);
            await player.InvokeViewPlugInAsync<IShowLoginResultPlugIn>(p => p.ShowLoginResultAsync(LoginResult.ConnectionError)).ConfigureAwait(false);
        }

        return reloadedAccount;
    }

The plugin was causing some persistence issues, so I deleted it and moved here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants